package main

import (
	"context"
	"fmt"
	"log/slog"
	"sync"
	"time"

	probing "github.com/prometheus-community/pro-bing"
	"github.com/tidwall/gjson"
)

// scrapeAllDevices 抓取所有设备
func scrapeAllDevices(ctx context.Context, devices []*RDLDevice) {
	var wg sync.WaitGroup

	for _, dev := range devices {
		wg.Go(func() {
			scrapeSingleDevice(ctx, dev)
		})
	}

	wg.Wait()
}

// scrapeSingleDevice
func scrapeSingleDevice(ctx context.Context, device *RDLDevice) {
	// PING探测
	rtt, err := ping(device.Host, 3*time.Second)
	if err != nil {
		slog.Error("Ping unreachable, or you do not have that privilege", "host", device.Host, "err", err.Error())
		MetricOnlineStatus.WithLabelValues(device.PrometheusLabelValues()...).Set(0)
		return
	}
	if rtt < 0 {
		slog.Error("Ping unreachable", "host", device.Host, "err", err.Error())
		MetricOnlineStatus.WithLabelValues(device.PrometheusLabelValues()...).Set(0)
		return
	}
	MetricOnlineStatus.WithLabelValues(device.PrometheusLabelValues()...).Set(1)
	MetricPingResponseTime.WithLabelValues(device.PrometheusLabelValues()...).Observe(rtt.Seconds())

	// 开始抓取
	var deviceJSON, portsJSON *gjson.Result
	for _, apiPth := range []string{"/device", "/ports"} {
		resultJSON, rtt, err := device.FetchJSON(ctx, apiPth)
		if err != nil {
			MetricScrapeFailuresTotal.WithLabelValues(append(device.PrometheusLabelValues(), apiPth)...).Inc()
			continue
		}

		MetricAPIResponseTime.WithLabelValues(append(device.PrometheusLabelValues(), apiPth)...).Observe(rtt.Seconds())

		switch apiPth {
		case "/device":
			deviceJSON = resultJSON
		case "/ports":
			portsJSON = resultJSON
		}
	}

	// 分析 /device 返回的数据填写相关指标
	// TODO: ...
	fmt.Println(deviceJSON)

	// 分析 /ports 返回的数据填写相关指标
	// TODO: ...
	fmt.Println(portsJSON)
}

// ping PING检查主机的响应时间
// will send an "unprivileged" ping via UDP. On Linux, this must be enabled with the command: `sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"`
func ping(host string, timeout time.Duration) (time.Duration, error) {
	pinger, err := probing.NewPinger(host)
	if err != nil {
		return -time.Nanosecond, err
	}
	pinger.Interval = 100 * time.Millisecond // 重发的间隔
	pinger.Count = 3                         // 重发的次数
	pinger.Timeout = timeout                 // 超时时间

	// 监听退出标志channel
	var (
		once        = new(sync.Once)
		chQuit      = make(chan struct{}, 1)
		closeChQuit = func() { once.Do(func() { close(chQuit) }) }
	)
	defer closeChQuit() // 避免 OnRecv 函数没有被调用的情况下通道泄漏

	var rtt time.Duration = -time.Nanosecond
	pinger.OnRecv = func(pkt *probing.Packet) {
		rtt = pkt.Rtt
		closeChQuit()
	}

	return rtt, pinger.Run()
}
